/*
 * Copyright (c) 2018 Alyssa Rosenzweig <alyssa@rosenzweig.io>
 *
 * Copyright (c) 2012 Rob Clark <robdclark@gmail.com>
 * Copyright (c) 2011-2012 Luc Verhaegen <libv@codethink.co.uk>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sub license,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <EGL/egl.h>
#include <GLES3/gl3.h>
#include <GLES3/gl3ext.h>

static EGLint const config_attribute_list[] = {
	EGL_RED_SIZE, 8,
	EGL_GREEN_SIZE, 8,
	EGL_BLUE_SIZE, 8,
	EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
	EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
	EGL_DEPTH_SIZE, 8,
	EGL_NONE
};

static const EGLint context_attribute_list[] = {
	EGL_CONTEXT_CLIENT_VERSION, 2,
	EGL_NONE
};

static EGLDisplay display;
static EGLConfig config;
static EGLint num_config;
static EGLContext context;
static GLuint program;
const char *vertex_shader_source =
		"#version 300 es              \n"
		"in vec4 in_position;         \n"
		"\n"
		"out vec4 vTexCoord;          \n"
		"                             \n"
		"void main()                  \n"
		"{                            \n"
		"    gl_Position = in_position;\n"
		"    vTexCoord = ((in_position.xyxy) + 1.0) * vec4(0.5, -0.5, 1.0, 1.0); \n"
		"}                            \n";

const char *fragment_shader_source =
		"#version 300 es              \n"
		"precision highp float;     \n"
		"                             \n"
		"uniform highp sampler2D uTexture;  \n"
		"uniform sampler2D uTexture2;  \n"
		"in vec4 vTexCoord;           \n"
		"out vec4 FragColor;       \n"
		"                             \n"
		"void main()                  \n"
		"{                            \n"
		//"    FragColor = texture(uTexture, vTexCoord.xy*1.1) + texture(uTexture, vTexCoord.xy) + texture(uTexture, vTexCoord.xy*1.2) + texture(uTexture, vTexCoord.xy*1.3);\n"
		//" FragColor = texture(uTexture, mod(vTexCoord.xy * 2.0 * vec2(1.0, -1.0), 1.0));\n"
		" float y = texture(uTexture, mod(vTexCoord.xy - vec2(0.01, 0.01), 1.0)).r;\n"
		" FragColor = vec4(vec3(y), 1.0);"
		"}                            \n";

#  include <X11/Xlib.h>
#  include <X11/Xutil.h>
#  include <X11/keysym.h>

static EGLNativeDisplayType native_dpy;

static EGLDisplay
get_display(void)
{
	EGLDisplay display;
	EGLint egl_major, egl_minor;

	native_dpy = XOpenDisplay(NULL);

	display = eglGetDisplay(native_dpy);

	eglInitialize(display, &egl_major, &egl_minor);

	return display;
}

EGLSurface surface;

static EGLSurface make_window(EGLDisplay display, EGLConfig config, int width, int height)
{
	XVisualInfo *visInfo, visTemplate;
	int num_visuals;
	Window root, xwin;
	XSetWindowAttributes attr;
	unsigned long mask;
	EGLint vid;
	const char *title = "egl";

	eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &vid);

	/* The X window visual must match the EGL config */
	visTemplate.visualid = vid;
	visInfo = XGetVisualInfo(native_dpy, VisualIDMask, &visTemplate, &num_visuals);
	if (!visInfo) {
		exit(-1);
	}

	root = RootWindow(native_dpy, DefaultScreen(native_dpy));

	/* window attributes */
	attr.background_pixel = 0;
	attr.border_pixel = 0;
	attr.colormap = XCreateColormap(native_dpy,
			root, visInfo->visual, AllocNone);
	attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
	mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;

	xwin = XCreateWindow(native_dpy, root, 0, 0, width, height,
			0, visInfo->depth, InputOutput, visInfo->visual, mask, &attr);
	if (!xwin) {
		exit (-1);
	}

	XFree(visInfo);

	/* set hints and properties */
	{
		XSizeHints sizehints;
		sizehints.x = 0;
		sizehints.y = 0;
		sizehints.width  = width;
		sizehints.height = height;
		sizehints.flags = USSize | USPosition;
		XSetNormalHints(native_dpy, xwin, &sizehints);
		XSetStandardProperties(native_dpy, xwin,
				title, title, None, (char **) NULL, 0, &sizehints);
	}

	XMapWindow(native_dpy, xwin);

	surface = eglCreateWindowSurface(display, config, xwin, NULL);

	return surface;
}


static GLuint
get_program(const char *vertex_shader_source, const char *fragment_shader_source)
{
	GLuint vertex_shader, fragment_shader, program;
	GLint ret;

	vertex_shader = glCreateShader(GL_VERTEX_SHADER);

	glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
	glCompileShader(vertex_shader);

	glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &ret);

	fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);

	glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
	glCompileShader(fragment_shader);

	glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &ret);

	program = glCreateProgram();

	glAttachShader(program, vertex_shader);
	glAttachShader(program, fragment_shader);

	return program;
}

static void
link_program(GLuint program)
{
	GLint ret, len;
	GLenum binary_format;
	void *binary;

	glLinkProgram(program);

	glGetProgramiv(program, GL_LINK_STATUS, &ret);

	glUseProgram(program);
}

GLfloat vVertices[] = {
		// front
		-1.00, -1.00, 0.0,
		 1.00, -1.00, 0.0,
		-1.00,  1.00, 0.0,
		 1.00,  1.00, 0.0
};

unsigned width;
unsigned height;

void
gles_display_init(unsigned _width, unsigned _height)
{
	GLint gwidth, gheight;
	GLuint textures[2], texture_handle;

	width = _width;
	height = _height;

	EGLSurface surface;

	display = get_display();

	eglChooseConfig(display, config_attribute_list, &config, 1, &num_config);

	context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attribute_list);

	surface = make_window(display, config, 1366, 768);

	eglQuerySurface(display, surface, EGL_WIDTH, &gwidth);
	eglQuerySurface(display, surface, EGL_HEIGHT, &gheight);

	/* connect the context to the surface */
	eglMakeCurrent(display, surface, surface, context);

	program = get_program(vertex_shader_source,  fragment_shader_source);

	glBindAttribLocation(program, 0, "in_position");

	link_program(program);

	glViewport(0, 0, 1366, 768); 

	glGenTextures(1, &textures);

	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, textures[0]);

	texture_handle = glGetUniformLocation(program, "uTexture");
	glUniform1i(texture_handle, 0); /* '0' refers to texture unit 0. */


	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT);

#if 0
	eglDestroySurface(display, surface);

	eglTerminate(display);
#endif
	printf("OK\n");
	return;
}


void
gles_display_yuv(
		uint8_t *plane_y, size_t pitch_y,
		uint8_t *plane_u, size_t pitch_u,
		uint8_t *plane_v, size_t pitch_v)
{
	glClearColor(0.5, 0.5, 0.5, 1.0);
	glClear(GL_COLOR_BUFFER_BIT);

	glTexImage2D(
			GL_TEXTURE_2D, 0, GL_RED,
			width, height, 0,
			GL_RED, GL_UNSIGNED_BYTE, plane_y);


	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vVertices);
	glEnableVertexAttribArray(0);

	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
	eglSwapBuffers(display, surface);
}
